' Elemental
' (c) Tesuji 2008
SuperStrict

Framework BRL.blitz
Import BRL.glmax2d
'Import BRL.D3D7Max2d
Import BRL.max2d
Import BRL.pixmap
Import BRL.pngloader
Import BRL.jpgloader
Import BRL.retro
Import BRL.Map

SetGraphicsDriver GLMax2DDriver()
'Graphics 800,600,0
Graphics 800,600,0,0,2

SeedRnd MilliSecs()

Global width:Int=512,height:Int=512
Global px:Particle[width+2,height+2]
Global centerX:Int = GraphicsWidth()/2
Global centerY:Int = GraphicsHeight()/2
Global halfWidth:Int = width/2
Global halfHeight:Int = height/2
Global xOrigin:Int = centerX-halfWidth
Global yOrigin:Int = centerY-halfHeight
Global particles:TList = CreateList()
Global menuItems:TList = CreateList()

Const OUTPUT_DIRECTORY:String = "images"

Const RENDERMODE_PIXEL:Int = 0
Const RENDERMODE_BLOB:Int = 1
Const RENDERMODE_BLOB2:Int = 2
Const RENDERMODE_IMAGE:Int = 3
Const MAX_RENDERMODES:Int = 4

Const DRAWMODE_PEN:Int = 0
Const DRAWMODE_LINE:Int = 1
Const DRAWMODE_CIRCLE:Int = 2
Const MAX_DRAWMODES:Int = 3

Const ITEMGROUP_ELEMENTS:Int = 0
Const ITEMGROUP_BRUSH:Int = 1
Const ITEMGROUP_CONTROL:Int = 2

Const MAX_FPS_HISTORY:Int = 60


Const ELEMENT_EMPTY:Int = 0
Const ELEMENT_WATER:Int = 1
Const ELEMENT_CONCRETE:Int = 2
Const ELEMENT_SALT:Int = 3
Const ELEMENT_MAGMA:Int = 4
Const ELEMENT_FIRE:Int = 5
Const ELEMENT_OIL:Int = 6
Const ELEMENT_SAND:Int = 7
Const ELEMENT_SMOKE:Int = 8
Const ELEMENT_GRAPHITE:Int = 9
Const ELEMENT_EMBER:Int = 10
Const ELEMENT_STEAM:Int = 11
Const ELEMENT_ACID:Int = 12
Const ELEMENT_WOOD:Int = 13
Const ELEMENT_SEED:Int = 14
Const ELEMENT_SPROUT:Int = 15
Const ELEMENT_GUNPOWDER:Int = 16
Const ELEMENT_MISSILE:Int = 17
Const ELEMENT_GRASS:Int = 18

Const MAX_ELEMENTS:Int = 19
'                                 0              1              2              3              4              5              6              7              8              9              10            11             12             13             14              15           16
Global elementLabels:String[] = [ "Air",         "Water",       "Concrete",    "Salt",        "Magma",       "Fire",        "Oil",         "Sand",        "Smoke",       "Graphite",    "Ember",      "Steam",       "Acid",        "Wood",        "Seed",        "Sprout",     "Gunpowder",    "Missile",      "Grass"         ]
Global elementCreatable:Int[] = [ True,          True,          True,          True,          True,          True,          True,          True,          False,         True,          False,        False,         True,          True,          True,          False,        True,           False,          False           ]
Global elementBitmask:Int[]   = [ $00000000,     $00000001,     $00000002,     $00000004,     $00000008,     $00000010,     $00000020,     $00000040,     $00000080,     $00000100,     $00000200,    $00000400,     $00000800,     $00001000,     $00002000,     $00004000,    $00008000,      $00010000,      $00020000       ] 
Global lifespan:Int[]         = [ -1,            -1,            -1,            -1,            -1,            128,           -1,            -1,            128,           -1,            64,           255,           -1,            128,           -1,            300,          -1,             1255,           -1              ]
Global elementAfterlife:Int[] = [ ELEMENT_EMPTY, ELEMENT_STEAM, ELEMENT_EMPTY, ELEMENT_EMPTY, ELEMENT_EMPTY, ELEMENT_SMOKE, ELEMENT_EMPTY, ELEMENT_EMPTY, ELEMENT_EMPTY, ELEMENT_EMPTY, ELEMENT_FIRE, ELEMENT_EMPTY, ELEMENT_EMPTY, ELEMENT_WOOD,  ELEMENT_EMPTY, ELEMENT_SEED, ELEMENT_EMPTY,  ELEMENT_EMPTY,  ELEMENT_SEED    ] 
Global elementImmovable:Int[] = [ False,         False,         True,          False,         False,         False,         False,         False,         False,         True,          True,         False,         False,         True,          False,         False,        False,          False,          True            ]
Global elementDrag:Float[]    = [ 1.0,           0.95,          0.0,           0.1,           0.8,           0.9,           0.85,          0.5,           0.995,         0.0,           0.0,          0.395,         0.87,          0.0,           0.85,          0.85,         0.58,           0.0,            0.0             ] ' elementDrag
Global elementMass:Float[]    = [ 0.1,           0.3,           1.0,           1.8,           3.4,           0.52,          0.3,           3.2,           2.01,          1.0,           1.0,          2.01,          0.32,          1.0,           0.13,          0.013,        2.2,            0.5,            1.0             ] ' elementMass
Global gravity:Float[]        = [ 1.0,           2.0,           0.0,           2.0,           2.0,          -2.0,           1.0,           2.0,          -1.0,           0.0,           0.0,         -1.0,           1.0,           1.0,           0.4,           -0.01,        2.0,            0.1,            1.0             ] ' gravity
Global slippyness:Float[]     = [ 0.0,           1.0,           0.0,           0.9,           1.0,           0.7,           1.0,           0.9,           1.0,           0.0,           0.0,          1.0,           1.0,           0.0,           0.995,         0.35,         0.9,            1.0,            0.0             ]
Global airDamp:Float[]        = [ 1.0,           0.0,           0.0,           0.65,          0.0,           0.5,           0.0,           0.35,          0.85,          0.0,           0.0,          0.59,          0.0,           0.0,           0.79,          0.93,         0.35,           0.99,           0.0             ]
Global elementBounce:Float[]  = [ 0.0,           0.0,           0.0,           0.5,           0.0,           0.75,          0.0,           0.35,          0.85,          0.0,           0.0,          0.59,          0.0,           0.0,           0.40,          0.93,         0.35,           0.99,           0.0             ]
Global elementRed:Int[]       = [ $00,           $10,           $80,           $F0,           $C0,           $FF,           $75,           $BA,           $C0,           $30,           $8F,          $C0,           $20,           $65,           $65,           $75,          $10,            $F0,            $45             ]
Global elementGreen:Int[]     = [ $00,           $9A,           $80,           $F0,           $20,           $40,           $2A,           $A2,           $C0,           $30,           $00,          $C0,           $B0,           $3E,           $7E,           $9E,          $10,            $F0,            $8E             ]
Global elementBlue:Int[]      = [ $80,           $CE,           $80,           $F0,           $10,           $10,           $02,           $30,           $C0,           $30,           $00,          $E0,           $08,           $22,           $22,           $22,          $10,            $D0,            $12             ]


Global elementInteractions:Interaction[MAX_ELEMENTS,MAX_ELEMENTS]

'                  element           reacts with       to form           leaving behind  with a probability of n in 100000
Interaction.Create(ELEMENT_WATER,    ELEMENT_SALT,     ELEMENT_WATER,    ELEMENT_WATER,  185)
Interaction.Create(ELEMENT_WATER,    ELEMENT_MAGMA,    ELEMENT_STEAM,    ELEMENT_SAND,   2000)
Interaction.Create(ELEMENT_WATER,    ELEMENT_ACID,     ELEMENT_STEAM,    ELEMENT_WATER,  200)
Interaction.Create(ELEMENT_WATER,    ELEMENT_EMBER,    ELEMENT_STEAM,    ELEMENT_WATER,  10000)
Interaction.Create(ELEMENT_WATER,    ELEMENT_FIRE,     ELEMENT_EMPTY,    ELEMENT_STEAM,  5000)
Interaction.Create(ELEMENT_WATER,    ELEMENT_MISSILE,  ELEMENT_STEAM,    ELEMENT_STEAM,  50000)

Interaction.Create(ELEMENT_SALT,     ELEMENT_WATER,    ELEMENT_SALT,     ELEMENT_WATER,  105)

Interaction.Create(ELEMENT_GRAPHITE, ELEMENT_FIRE,     ELEMENT_FIRE,     ELEMENT_EMBER,  500)
Interaction.Create(ELEMENT_GRAPHITE, ELEMENT_MAGMA,    ELEMENT_MAGMA,    ELEMENT_FIRE,   2000)
Interaction.Create(ELEMENT_GRAPHITE, ELEMENT_EMBER,    ELEMENT_EMBER,    ELEMENT_EMBER,  25000)
Interaction.Create(ELEMENT_GRAPHITE, ELEMENT_MISSILE,  ELEMENT_SMOKE,    ELEMENT_EMBER,  25000)

Interaction.Create(ELEMENT_EMBER,    ELEMENT_GRAPHITE, ELEMENT_EMBER,    ELEMENT_EMBER,  25000)
Interaction.Create(ELEMENT_EMBER,    ELEMENT_OIL,      ELEMENT_FIRE,     ELEMENT_SMOKE,  50000)
Interaction.Create(ELEMENT_EMBER,    ELEMENT_SAND,     ELEMENT_MAGMA,    ELEMENT_EMBER,  250)
Interaction.Create(ELEMENT_EMBER,    ELEMENT_WOOD,     ELEMENT_FIRE,     ELEMENT_EMBER,  2500)
Interaction.Create(ELEMENT_EMBER,    ELEMENT_SEED,     ELEMENT_FIRE,     ELEMENT_FIRE,   2500)
Interaction.Create(ELEMENT_EMBER,    ELEMENT_GRASS,    ELEMENT_FIRE,     ELEMENT_FIRE,   2500)

Interaction.Create(ELEMENT_MAGMA,    ELEMENT_WATER,    ELEMENT_STEAM,    ELEMENT_MAGMA,  10000)
Interaction.Create(ELEMENT_MAGMA,    ELEMENT_ACID,     ELEMENT_EMBER,    ELEMENT_MAGMA,  150)
Interaction.Create(ELEMENT_MAGMA,    ELEMENT_WOOD,     ELEMENT_FIRE,     ELEMENT_MAGMA,  2150)
Interaction.Create(ELEMENT_MAGMA,    ELEMENT_SEED,     ELEMENT_FIRE,     ELEMENT_MAGMA,  3150)
Interaction.Create(ELEMENT_MAGMA,    ELEMENT_GRASS,    ELEMENT_FIRE,     ELEMENT_MAGMA,  4150)
Interaction.Create(ELEMENT_MAGMA,    ELEMENT_GUNPOWDER,ELEMENT_FIRE,     ELEMENT_MAGMA,  10000)
Interaction.Create(ELEMENT_MAGMA,    ELEMENT_MISSILE,  ELEMENT_SMOKE,    ELEMENT_MAGMA,  5000)

Interaction.Create(ELEMENT_FIRE,     ELEMENT_WATER,    ELEMENT_EMPTY,    ELEMENT_STEAM,  10000)
Interaction.Create(ELEMENT_FIRE,     ELEMENT_GRAPHITE, ELEMENT_EMBER,    ELEMENT_SMOKE,  2500)
Interaction.Create(ELEMENT_FIRE,     ELEMENT_SAND,     ELEMENT_MAGMA,    ELEMENT_SMOKE,  150)
Interaction.Create(ELEMENT_FIRE,     ELEMENT_WOOD,     ELEMENT_FIRE,     ELEMENT_GRAPHITE,  2000)
Interaction.Create(ELEMENT_FIRE,     ELEMENT_GRASS,    ELEMENT_FIRE,     ELEMENT_FIRE,  1500)
Interaction.Create(ELEMENT_FIRE,     ELEMENT_SEED,     ELEMENT_FIRE,     ELEMENT_EMBER,  500)
Interaction.Create(ELEMENT_FIRE,     ELEMENT_GUNPOWDER,ELEMENT_FIRE,     ELEMENT_FIRE,   15000)

Interaction.Create(ELEMENT_OIL,      ELEMENT_FIRE,     ELEMENT_FIRE,     ELEMENT_FIRE,   15000)
Interaction.Create(ELEMENT_OIL,      ELEMENT_MAGMA,    ELEMENT_FIRE,     ELEMENT_FIRE,   5000)
Interaction.Create(ELEMENT_OIL,      ELEMENT_MISSILE,  ELEMENT_SMOKE,    ELEMENT_FIRE,   25000)

Interaction.Create(ELEMENT_ACID,     ELEMENT_GRAPHITE, ELEMENT_STEAM,    ELEMENT_ACID,   200)
Interaction.Create(ELEMENT_ACID,     ELEMENT_CONCRETE, ELEMENT_GRAPHITE, ELEMENT_ACID,   225)
Interaction.Create(ELEMENT_ACID,     ELEMENT_OIL,      ELEMENT_STEAM,    ELEMENT_ACID,   900)
Interaction.Create(ELEMENT_ACID,     ELEMENT_SAND,     ELEMENT_STEAM,    ELEMENT_ACID,   2500)
Interaction.Create(ELEMENT_ACID,     ELEMENT_SALT,     ELEMENT_STEAM,    ELEMENT_ACID,   8500)
Interaction.Create(ELEMENT_ACID,     ELEMENT_WATER,    ELEMENT_STEAM,    ELEMENT_ACID,   200)
Interaction.Create(ELEMENT_ACID,     ELEMENT_WOOD,     ELEMENT_GRAPHITE, ELEMENT_ACID,   10000)
Interaction.Create(ELEMENT_ACID,     ELEMENT_MISSILE,  ELEMENT_STEAM,    ELEMENT_ACID,   40000)
Interaction.Create(ELEMENT_ACID,     ELEMENT_GUNPOWDER,ELEMENT_STEAM,    ELEMENT_ACID,   400)
Interaction.Create(ELEMENT_ACID,     ELEMENT_GRASS,    ELEMENT_STEAM,    ELEMENT_ACID,   4000)


Interaction.Create(ELEMENT_WOOD,     ELEMENT_FIRE,     ELEMENT_FIRE,     ELEMENT_EMBER,  5000)
Interaction.Create(ELEMENT_WOOD,     ELEMENT_EMBER,    ELEMENT_FIRE,     ELEMENT_EMBER,  1000)
Interaction.Create(ELEMENT_WOOD,     ELEMENT_MISSILE,  ELEMENT_FIRE,     ELEMENT_EMBER,  1000)

Interaction.Create(ELEMENT_GUNPOWDER, ELEMENT_FIRE,    ELEMENT_FIRE,     ELEMENT_FIRE,     5000)
Interaction.Create(ELEMENT_GUNPOWDER, ELEMENT_MAGMA,   ELEMENT_FIRE,     ELEMENT_FIRE,     15000)
Interaction.Create(ELEMENT_GUNPOWDER, ELEMENT_MISSILE, ELEMENT_FIRE,     ELEMENT_FIRE,     50000)


Global selectedElement:Int = ELEMENT_WATER
Global renderMode:Int = RENDERMODE_BLOB
' todo - double sized pixels seem too much for the default windows OGL drivers in my old laptop. Need a dx fast plot really.
'?Win32
'renderMode = RENDERMODE_PIXEL
'?
Global drawMode:Int = DRAWMODE_PEN
Global frame:Int = 0
Global vSync:Int = 0
Global simulationRunning:Int = True
Global displayBackground:Int = True
Global loadSelectMode:Int = False
Global saveSelectMode:Int = False

Const MAX_PEN_SIZE:Int = 3
Global penSizes:Int[] = [1,3,5]
Global selectedPenSize:Int = 1
Global alphaByte:Byte

' -----------------------------------------------------------

Global fpstime:Long
Global fpsHistory:Int[MAX_FPS_HISTORY]

Global randomNumbers1:Float[1000000]
Global randomNumbersIndex1:Int = 0
Global randomNumbers2:Int[1000000]
Global randomNumbersIndex2:Int = 0
Global randomNumbers3:Float[1000000]
Global randomNumbersIndex3:Int = 0
For Local i:Int = 0 To 999999
	randomNumbers1[i] = Rnd(-1.0,1.0)
	randomNumbers2[i] = Rand(0,100000)
	randomNumbers3[i] = Rnd(0.0,1.0)
Next

Global prevMouseX:Int, prevMouseY:Int
Global drag:Int = False, dragFromX:Int, dragFromY:Int

Global blobImage:TImage = createBlobImage()
Global backgroundImage:TImage = createGradientImage(width,height)
Global menuImage:TImage = createMenuImage()
Global zoomImage:TImage = CreateImage(64,64,1,DYNAMICIMAGE + FILTEREDIMAGE)
Global selectedImageIndex:Int = 0

Global savedGamePixmap:TPixmap
createSaveGameDir()
initParticles()
reflectStateInMenu()

' ------------------------------------------------------------
' mainloop
' ------------------------------------------------------------
Function Main()
While Not KeyHit(KEY_ESCAPE)

    Cls

	SetOrigin centerX, centerY
	
	If displayBackground = True
		SetColor 0,24,96
		SetBlend SOLIDBLEND
		SetImageHandle backgroundImage,halfWidth,halfHeight
		DrawImage backgroundImage,0,-1
	End If
	
	If loadSelectMode = True
		savedGamePixmap = selectSavedFile()
		If savedGamePixmap <> Null Particle.initFromPixmap(savedGamePixmap)
		loadSelectMode = False
		reflectStateInMenu()
	Else If saveSelectMode = True
		savedGamePixmap = Particle.toPixmap() 
		savePixmap(savedGamePixmap)
		saveSelectMode = False
		selectedImageIndex = 0
		reflectStateInMenu()
	Else
   
	    ' process and render particles
		Particle.preRender()
		If simulationRunning = True
			For Local p:Particle = EachIn particles		
				p.update()
				p.interact()
				p.render()
			Next
		Else		
			For Local p:Particle = EachIn particles		
				p.render()
			Next
		End If
		Particle.postRender()
	
	End If
	
   SetColor 255,255,255	
   SetOrigin 0,0
   SetScale 1,1
   SetBlend ALPHABLEND
   SetAlpha 1
 
   DrawText  "FPS: " + countFps(),16+(xOrigin),6
   DrawText  CountList(particles),(xOrigin)+width-64,6
   userInput()
   updateMenu()
   If KeyDown(KEY_LCONTROL) Or simulationRunning = False Then displayZoom()
   If KeyDown(KEY_LCONTROL) And KeyHit(KEY_G) Then 	screenshot()
   
   Flip vSync
   frame = (frame + 1) Mod 50000
	If randomNumbersIndex1 > 500000 randomNumbersIndex1 = 0
	If randomNumbersIndex2 > 500000 randomNumbersIndex2 = 0
	If randomNumbersIndex3 > 500000 randomNumbersIndex3 = 0
   Delay 1
Wend
End
End Function
Main()

' --------------------------------------------------------------------------------------

' ------------------------------------------
' Simple 2D Image Sprite with z scaling.
' ------------------------------------------
Type TSprite

	Global sprites:TList = CreateList()
	Field x:Float,y:Float,z:Float
	Field image:TImage
	Field reflection:Int = True
	
	Function Create:TSprite(image:TImage, x:Float=0,y:Float=0,z:Float=0)
	
		Local s:TSprite = New TSprite
		SetImageHandle image, ImageWidth(image)/2, ImageHeight(image)/2 
		s.image = image
		s.x = x
		s.y = y
		s.z = z
		ListAddLast sprites,s

		Return s
		
	End Function
	
	Function deleteAll()
		ClearList(sprites)
	End Function

	' render all sprites in correct z order	
	Function renderAll()
	
		SetOrigin GraphicsWidth()/2, GraphicsHeight()/2
	
		sprites.sort()
		For Local s:TSprite = EachIn sprites
			s.render()
		Next
		
		SetOrigin 0,0
	
	End Function
	
	' sort by zOrder
	Method Compare:Int(other:Object)
		Local s:TSprite = TSprite(other)
		If z < s.z Return -1
		If z = s.z Return 0
		If z > s.z Return 1
	End Method
	
	Method render()
	
		Local scale:Float = 2^z
		Local sx:Int = x*(GraphicsWidth()/2)*scale
		Local sy:Int = y*(GraphicsHeight()/2)*scale
		SetScale scale,scale
		DrawImage image,sx,sy
		
		If reflection
			Local alpha:Float = GetAlpha()
			Local blend:Int = GetBlend() 
			
			SetAlpha alpha*.5
			SetScale scale,-scale
			DrawImage image,sx,sy+((ImageHeight(image)*scale))+1
			SetBlend SHADEBLEND
			SetAlpha 1.0
			DrawImage backgroundImage, sx, sy+1+((ImageHeight(backgroundImage)*scale))
			
			SetAlpha alpha
			SetBlend blend
			
		End If
			
	End Method
	
	Method remove()
		sprites.remove(Self)
	End Method
	

End Type

' -----------------------------
' Wrapper class for Element
' -----------------------------

Type TElement
	Field id:Int
	
	Function Create:TElement(id:Int)
		Local e:TElement = New TElement
		e.id = id
		Return e
	End Function
	
End Type


' ------------------------------
' File System object
' ------------------------------
Type TFile

    Field path:String
    Field filename:String
    Field modified:Int
    Field size:Int
	Field isLoaded:Int = False

    Function Create:TFile(filepath:String,filename:String,modified:Int,size:Int)

        Local file:TFile = New TFile
        file.path = filepath
        file.filename = filename
        file.modified = modified
        file.size = size

        Return file

    End Function

	Method toString:String()
		Return path +"/"+filename+" "+modified+" "+size+" "+isLoaded
	End Method

	' sort by last modified timestamp
	Method Compare:Int(otherObject:Object)
		Local file:TFile = TFile(otherObject)
		Return file.modified - Self.modified
	End Method


End Type

' ----------------------------------
' Element Particle
' ----------------------------------

Type Particle

	Field element:Int
	Field x:Int,y:Int
	Field xp:Float, yp:Float, xv:Float, yv:Float
	Field mass:Float
	Field life:Int
	Field energy:Int
	Field link:TLink
	Field asleep:Int = 0
	
	Function Create:Particle(element:Int, x:Int, y:Int, xv:Float=0.0, yv:Float=0.0)
	
		If element = ELEMENT_EMPTY
			clear(x,y)	
		Else If x > 0 And x < width And y > 0 And y < height And px[x,y] = Null
			Local p:Particle = New Particle
			p.x = x
			p.y = y
			p.xp = x
			p.yp = y+.99
			p.energy = 0
			p.init(element,xv,yv)
			px[x,y] = p
			p.link = particles.AddLast(p)
			'Print "created particle : " + p.toString()
			Return p
		End If
		
	End Function
	
	Function reset()
		For Local x:Int = 0 To width-1
			For Local y:Int = 0 To height-1
				If px[x,y] <> Null Particle.clear(x,y)
			Next
		Next
		
		ClearList particles
	End Function
	
	Function clear(x:Int,y:Int)
		If x >= 0 And x < width And y >= 0 And y < height
			Local p:Particle = px[x,y]
			If p <> Null
				px[x,y] = Null
				p.remove() 
			End If
		End If
	End Function
	
	Function toPixmap:TPixmap()
	
		Local pixmap:TPixmap = CreatePixmap(width,height, PF_RGBA8888)
		ClearPixels(pixmap, $00000080)
		
		For Local y:Int = 0 To height-1
			Local pb:Byte Ptr = PixmapPixelPtr(pixmap,0,y)
			Local alpha:Byte = 128+(y/4) 
			For Local x:Int = 0 To width-1
				pb[(x*4)+3] = alpha
			Next
		Next 
		
		For Local p:Particle = EachIn particles
			Local pb:Byte Ptr = PixmapPixelPtr(pixmap,p.x,p.y)
			pb[0] = elementRed[p.element]
			pb[1] = elementGreen[p.element]
			pb[2] = elementBlue[p.element]
			pb[3] = 255
		Next
		
		Return pixmap
	
	End Function
	
	Function initFromPixmap(pixmap:TPixmap)

		Local red:Byte
		Local green:Byte
		Local blue:Byte
		Local colorKey:String
		Local element:Int
		Local e:TElement
	
		' create colour -> element lookup map		
		Local colorToElementMap:TMap = CreateMap()
		For element = 0 To MAX_ELEMENTS-1
			e = TElement.Create(element)
			colorKey = rgbToString(elementRed[element],elementGreen[element],elementBlue[element])
			MapInsert(colorToElementMap, colorKey, e)
		Next
	
		Particle.reset()
		For Local y:Int = 0 To height-1
			Local pb:Byte Ptr = PixmapPixelPtr(pixmap,0,y)
			For Local x:Int = 0 To width-1
				red   = pb[(x*4)+0]
				green = pb[(x*4)+1]
				blue  = pb[(x*4)+2]
				colorKey = rgbToString(red,green,blue)				
				e = TElement(MapValueForKey(colorToElementMap, colorKey))
				
				'Local approx:Int = (((red + green + blue) /3)/8)
				'If approx < MAX_ELEMENTS Then Particle.Create(approx,x,y)
				If e <> Null And e.id <> ELEMENT_EMPTY Then Particle.Create(e.id,x,y)
			Next
		Next 		
	
	End Function
	
	Function preRender() 
	
		Select renderMode
			Case RENDERMODE_PIXEL
				SetBlend ALPHABLEND
				alphaByte = 255
				glDisable GL_TEXTURE_2D
				glPointSize 1.0
				glBegin GL_POINTS

			Case RENDERMODE_BLOB
				SetBlend ALPHABLEND
				alphaByte = 128
				glDisable GL_TEXTURE_2D
				glPointSize 2.0 
				glBegin GL_POINTS

			Case RENDERMODE_BLOB2
				SetBlend ALPHABLEND
				alphaByte = 64
				glDisable GL_TEXTURE_2D
				glPointSize 2.0 
				glBegin GL_POINTS
				
			Case RENDERMODE_IMAGE
				SetBlend LIGHTBLEND
				alphaByte = 64
				SetScale .5,.5
		
		End Select
	
	End Function
	
	Function postRender()
	
		Select renderMode

			Case RENDERMODE_IMAGE
				SetScale 1,1
				
			Default
				glEnd
				glEnable GL_TEXTURE_2D
				
		End Select

	End Function
	
	Method init(targetElement:Int, xVelocity:Float=0.0, yVelocity:Float=0.0)
		element = targetElement
		life = lifespan[element]
		If life > 0 life = Rand(life/2,life)
		xv = xVelocity
		yv = yVelocity
		'mass = Rnd(elementMass[element]*.5, elementMass[element]*2.0)
		mass = (elementMass[element]*.5) + (randomNumbers3[randomNumbersIndex3] * elementMass[element]*1.5) 
		randomNumbersIndex3 :+ 1
	End Method
	
	Method remove()
		link.remove()
	End Method
	
	Method toString:String()
		Return "[element:"+element+" life:"+life+" x:"+x+" y:"+y+"]"
	End Method
	
	Method airmove()

		' Limit velocity
		If yv > 1.0 Then 
			yv = 1.0
		Else If yv < -1.0 Then 
			yv = -1.0
		End If

		' Increase position
		If yv > 1.0 Then 
			yp :+ 1.0
		Else If yv < -1.0 Then 
			yp :- 1.0
		Else
			yp :+ yv
		End If
	
		' Move in pixel pos if float pos no longer equal
		If Int(yp) <> y
			px[x,y] = Null 'air
			y = yp 'new pos
			If y >= height Or y < 1 Then 'out of screen, delete
				remove()
			Else
				px[x,y] = Self 'new pos
			End If
		End If
		' apply air friction
		yv :+ (mass * gravity[element])*.005
	
	End Method
	
	Method move(neighbour:Particle)
	
		If elementImmovable[neighbour.element]
			yv = -yv * elementBounce[element]
		Else
	
			If neighbour.element <> element 
				
				' debug
'				If yv > 1.0 Then 
'					yv = 1.0
'				Else If yv < -1.0 Then 
'					yv = -1.0
'				End If
	
				
				If yv > 1.0 Then 
					yp :+ 1.0
				Else If yv < -1.0 Then 
					yp :- 1.0
				Else
					yp :+ yv
				End If
				If Int(yp) <> y
					px[x,y] = neighbour
					neighbour.y = y
					neighbour.yp = y
					y = yp
					If y >= height Or y < 1 Then 
						remove()
					Else
						px[x,y] = Self
					End If
				End If
				yv = (((mass * gravity[element])*.05) * elementDrag[neighbour.element])
	
			Else
				neighbour.yv :+ (yv*.05) ' impart velocity to neighbour 
				yv = yv * .95 ' slow down if next to same element
			End If
		
		End If
	
	End Method

	
	Method update()

		If life >= 0 Then 
			life :- 1
			If life <= 0 Then
				If elementAfterlife[element] = ELEMENT_EMPTY 
					Particle.clear(x,y)
				Else If element <> elementAfterlife[element]
					changeElement(Self, elementAfterlife[element], xv, yv)
				End If 
				Return
			End If
		End If

		Local neighbour:Particle
		Local nextTo:Particle	

		
		Select element
		
			' solids don't move
			Case ELEMENT_CONCRETE
			Case ELEMENT_GRAPHITE
			Case ELEMENT_EMBER
			Case ELEMENT_WOOD
			Case ELEMENT_GRASS
			
			Case ELEMENT_MISSILE
			
				Local oyp:Float = yp
				If yv > 1.0 Then 
					yp :+ 1.0
				Else If yv < -1.0 Then 
					yp :- 1.0
				Else
					yp :+ yv
				End If
				
				Local oxp:Float = xp
				If xv > 1.0 Then 
					xp :+ 1.0
				Else If xv < -1.0 Then 
					xp :- 1.0
				Else
					xp :+ xv
				End If
				
				If Int(yp) <> y
					If px[x,yp] = Null
						px[x,y] = Null
						If life < lifespan[element]/2
							If life Mod 3 = 0 Then 							Particle.Create(ELEMENT_FIRE,oxp,oyp,-xv*.25,-yv*.25)
						Else
							Particle.Create(ELEMENT_FIRE,oxp,oyp,-xv*.25,-yv*.25)
						End If
						y = yp
						px[x,y] = Self
					Else
						yp = oyp
						'yv = -yv
					End If
					
				End If

				If Int(xp) <> x
					If px[xp,y] = Null
						px[x,y] = Null
						'Particle.Create(ELEMENT_EMBER,oxp,oyp,xv,yv)
						x = xp
						px[x,y] = Self
					Else
						xp = oxp
						'xv = -xv
					End If
				End If
				
				yv :+ .005
				' friction
				xv :* .998
				yv :* .998
			
					
			Default
				' Y-movement			
				If yv > 0 
					If px[x,y+1] = Null Then 
						airmove()				
					Else 
						move(px[x,y+1])
					End If			
					
				Else If yv < 0 
					If  px[x,y-1] = Null Then 
						airmove()
					Else 
						move(px[x,y-1])
					End If
				Else 
					yv :+ ((mass * gravity[element])*.005)
				End If
				
				' X-movement
				If px[x,y+1] <> Null
					' element below
				 	randomNumbersIndex2 :+1
					If xv = 0 Or (slippyness[element] = 1.0 And randomNumbers2[randomNumbersIndex2] < 10000)
						randomNumbersIndex1 :+1
						Local slip:Float = Sgn(randomNumbers1[randomNumbersIndex1])
						If px[x+slip,y] = Null Then xv = slip
					End If
					xv :* slippyness[element]

				Else
					' air below
					randomNumbersIndex1 :+1		
					xv :+ randomNumbers1[randomNumbersIndex1] * slippyness[element]
					xv :* airDamp[element]

				End If	 

				' slide
				
				' debug
'				If xv > 1.0 Then 
'					xv = 1.0
'				Else If xv < -1.0 Then 
'					xv = -1.0
'				End If

				
				If xv > 1.0 Then 
					xp :+ 1.0
					If xv > 8.0 xv = 8.0 
				Else If xv < -1.0 Then 
					xp :- 1.0
					If xv < -8.0 xv = -8.0
				Else
					xp :+ xv
				End If											

				'xp :+ xv
				If Int(xp) <> x
					nextTo = px[xp,y]
					If nextTo = Null
						px[x,y] = Null
						x = xp
						yp = y+.5
						If px[x,y+1] = Null Then 
							yv :+  slippyness[element]	 * Sgn gravity[element] * .15 
						End If
						If x > 1 And x < width-1
							px[x,y] = Self
						Else
							remove()
						End If
					Else
						If nextTo.element <> element
							nextTo.xv :+ (xv*elementDrag[nextTo.element]*2.0)
						End If
						xv = -xv						
						xp = x
					End If			
				End If
				
		
		End Select
		
	
	End Method

	Method interact()
			
		Select element
		
            Case ELEMENT_WATER
				interactWithNeighbours()								
			Case ELEMENT_GRAPHITE
				interactWithNeighbours()				
			Case ELEMENT_EMBER
				interactWithNeighbours()			
            Case ELEMENT_MAGMA
				interactWithNeighbours()				
            Case ELEMENT_FIRE
				interactWithNeighbours()
			Case ELEMENT_GUNPOWDER
				interactWithNeighbours()
				If element = ELEMENT_FIRE
					' explode
					Local a:Int = Rand(0,360)
					Particle.Create(ELEMENT_MISSILE,x,y-1,Sin(a),Cos(a)*2.0)
					xv = Rnd(-1.0,1.0)*4.0
					yv = Rnd(-1.0,1.0)*4.0
				End If
            Case ELEMENT_OIL
				interactWithNeighbours()
            Case ELEMENT_ACID
				interactWithNeighbours()
            Case ELEMENT_WOOD
				If getNeighbourElements() <> 0
					If px[x,y+1] <> Null
						Local p:Particle = px[x,y+1]
						If p.element = ELEMENT_WATER And Rand(0,200) = 0 Then changeElement(p,ELEMENT_WOOD,0,0)
					End If
					interactWithNeighbours()
				Else If life > 0
					Particle.Create(ELEMENT_GRAPHITE,x-1,y)
					Particle.Create(ELEMENT_GRAPHITE,x+1,y)
				Else
					changeElement(Self, ELEMENT_SMOKE, 0, 0)
				End If
				
			Case ELEMENT_SEED

				If frame & 1 = 0
					Local match:Int = elementBitmask[ELEMENT_SAND] | elementBitmask[ELEMENT_WATER]
					If (getNeighbourElements() & match) = match Then
						If Rand(0,10) = 0
							changeElement(Self, ELEMENT_SPROUT, 0, 0)
							energy = Rand(296,400)
						Else
							changeElement(Self, ELEMENT_GRASS, Rnd(-.2,.2), -1.0)
							energy = Rand(10,20) 
						End If
					End If
				Else
					Local match:Int = elementBitmask[ELEMENT_WOOD] | elementBitmask[ELEMENT_WATER]
					If (getNeighbourElements() & match) = match Then
						If Rand(0,2) = 0
							changeElement(Self, ELEMENT_SPROUT, 0, 0)
							energy = Rand(100,200)
						Else
							changeElement(Self, ELEMENT_GRASS, Rnd(-.5,.5), -.5)
							energy = Rand(5,10)
						End If
					End If

				End If
			
			Case ELEMENT_SPROUT
				
				If energy > 0 And life > 0
					If energy Mod 24 = 13
						' branch
						Local p:Particle = Particle.Create(ELEMENT_SPROUT,x+xv,y-1,-xv,-(energy/300.0))
						If p <> Null 
							p.life = life
							p.energy = energy-1
						End If	
					End If
		
					Particle.Create(ELEMENT_WOOD,x,y+1) ; energy :- 1
					If energy > 196 Then Particle.Create(ELEMENT_WOOD,x-1,y+1) ; energy :- 1
					If energy > 128 Then Particle.Create(ELEMENT_WOOD,x+1,y+1) ; energy :- 1
					
				End If
				
			Case ELEMENT_GRASS
			
				If energy > 0 And Rand(0,10) = 0
					
					If xv > 1.0 xv = 1.0
					If xv < -1.0 xv = -1.0
					xp :+ xv
					If yv > 1.0 yv = 1.0
					If yv < -1.0 yv = -1.0
					yp :+ yv

					If px[xp,yp] = Null
						Local p:Particle = Particle.Create(ELEMENT_GRASS, xp, yp, xv*1.05, yv*.95)
						If p <> Null 
							p.energy = energy-1
							p.xp = xp
							p.yp = yp
						End If	
						xp = x
						yp = y
						energy = 0
					Else
						If Int(xp) <> x Then xp :- xv
						If Int(yp) <> y Then yp :- yv
						energy :-1
					End If

					
				End If
							
		End Select
	
	End Method
	
	Method getNeighbourElements:Int()
	
		Local pTop:Particle = px[x,y-1]
		Local pBottom:Particle = px[x,y+1]
		Local pLeft:Particle = px[x-1,y]
		Local pRight:Particle = px[x+1,y]
		
		Local bitmask:Int = 0
		If pTop    <> Null bitmask :| elementBitmask[pTop.element]
		If pBottom <> Null bitmask :| elementBitmask[pBottom.element]
		If pLeft   <> Null bitmask :| elementBitmask[pLeft.element]
		If pRight  <> Null bitmask :| elementBitmask[pRight.element]

		Return bitmask
	
	End Method
	
	Method interactWithNeighbours()
		Local pTop:Particle = px[x,y-1]
		Local pBottom:Particle = px[x,y+1]
		Local pLeft:Particle = px[x-1,y]
		Local pRight:Particle = px[x+1,y]
		
		If pTop <> Null And pTop.element <> element Then interactWith(pTop)	
		If pBottom <> Null And pBottom.element <> element Then interactWith(pBottom)
		If pLeft <> Null And pLeft.element <> element Then interactWith(pLeft)
		If pRight <> Null And pRight.element <> element Then interactWith(pRight) 				
	End Method
	
	Method interactWith(p:Particle)

		If elementInteractions[element, p.element] <> Null
			Local i:Interaction = elementInteractions[element, p.element]
			randomNumbersIndex2 :+1
			If randomNumbers2[randomNumbersIndex2] < i.spreadChance Then 
				changeElement(p, i.spreadElement)
				changeElement(Self, i.residueElement)
			End If
		End If
	
	End Method
		
	Function changeElement(p:Particle, targetElement:Int, xVelocity:Float=0.0, yVelocity:Float=0.0)
		If targetElement = ELEMENT_EMPTY Then
				Particle.clear(p.x,p.y) 
		Else	
				p.init(targetElement,xVelocity,yVelocity)
		End If
	End Function		
				
	Method render()
	
	    Select element
	
			Case ELEMENT_EMBER
				glColor4ub life*5,life*2,life,alphaByte

            Case ELEMENT_FIRE
				glColor4ub 255,life*2,life/2,alphaByte
				
			Case ELEMENT_SMOKE
				glColor4ub life,life,life,alphaByte

			Case ELEMENT_STEAM
				glColor4ub 250,250,255,life
				
			Case ELEMENT_WOOD
				glColor4ub elementRed[element], elementGreen[element]+(life/2), elementBlue[element],((y*y)+x) | $60

			Case ELEMENT_GUNPOWDER
				glColor4ub $10+(160*yv*xv), $10+(160*yv*xv), $10+(160*yv*xv),alphaByte

			Case ELEMENT_MISSILE
				glColor4ub elementRed[element], elementGreen[element], elementBlue[element],(life/Float(lifespan[element]))*255

			

			Default 
				'glColor4ub elementRed[element]+(255*yv), elementGreen[element]+(255*yv), elementBlue[element]+(255*yv),alphaByte
				'glColor4ub elementRed[element]+(255*xv), elementGreen[element]+(255*xv), elementBlue[element]+(255*xv),alphaByte
				'glColor4ub elementRed[element]-asleep, elementGreen[element]-asleep, elementBlue[element]-asleep,alphaByte

				glColor4ub elementRed[element], elementGreen[element], elementBlue[element], alphaByte 				 
         End Select
	
		Select renderMode
		
			Case RENDERMODE_PIXEL
				glVertex2i x+xOrigin,y+yOrigin
			Case RENDERMODE_BLOB
				glVertex2i x+xOrigin,y+yOrigin
			Case RENDERMODE_BLOB2
				glVertex2i x+xOrigin,y+yOrigin
				glVertex2i x+xOrigin+1,y+yOrigin+1
				glVertex2i x+xOrigin,y+yOrigin+1
				glVertex2i x+xOrigin+1,y+yOrigin
			Case RENDERMODE_IMAGE
				DrawImage blobImage,x-halfWidth,y-halfHeight
		End Select
				
	End Method
	
End Type

Type Interaction

	Field spreadElement:Int
	Field residueElement:Int
	Field spreadChance:Int
	
	Function Create:Interaction(sourceElement:Int, neighbourElement:Int, spreadElement:Int, residueElement:Int, spreadChance:Int)
		
		Local i:Interaction = New Interaction
		i.spreadChance = spreadChance
		i.spreadElement = spreadElement
	    i.residueElement = residueElement
		elementInteractions[sourceElement, neighbourElement] = i
		Return i
		
	End Function

End Type

Type MenuItem

	Field x:Int, y:Int, w:Int, h:Int
	Field id:Int
	Field group:Int
	Field selected:Int = False
	Field recentlySelected:Int = False
	Field selectFade:Float = 0.0
	Field image:TImage
	Field selectedImage:TImage
	Field selectedImageXOffset:Int
	Field selectedImageScale:Float
	Field selectedReflectionImage:TImage
	Field buttonReleased:Int = True

	
	Function Create:MenuItem(x:Int,y:Int,w:Int,h:Int, id:Int, image:TImage, group:Int=0, selectedImage:TImage=Null, selectedImageScale:Float=.25, selectedImageXOffset:Int=0, selectedReflectionImage:TImage = Null )
		Local mi:MenuItem = New MenuItem
		mi.x = x
		mi.y = y
		mi.w = w
		mi.h = h
		mi.id = id
		mi.image = image
		mi.group = group
		menuItems.addLast(mi)
		If selectedImage = Null 
			mi.selectedImage = image
		Else
			mi.selectedImage = selectedImage
		End If
		mi.selectedImageScale = selectedImageScale
		mi.selectedImageXOffset = selectedImageXOffset
		mi.selectedReflectionImage = selectedReflectionImage

'		Print "created MenuItem [ x:"+x+" y:"+y+" w:"+w+" h:"+h+" id:"+id+" group:"+group+"]"
		Return mi
	End Function
	
	Function setSelectedItem(group:Int, id:Int)
		For Local mi:MenuItem = EachIn menuItems
			If mi.group = group And mi.id = id Then mi.selectItem()	
		Next 		
	End Function
	
	Method update()
		If buttonReleased = False
			If Not MouseDown(1) Then buttonReleased = True
		End If
		If selected = True Or selectFade > 0 Then highlightSelected()
		If MouseX() >= x And MouseX() < x+w And MouseY() >= y And MouseY() < y+h
			If buttonReleased = True And MouseDown(1)
				selectItem()
				recentlySelected = True
			End If
			highlightHover()
		End If
	End Method
	
	Method hasJustBeenSelected:Int()
		If recentlySelected = True Then
			recentlySelected = False
			buttonReleased = False
			Return True
		End If
	End Method
	
	Method highlightHover()
		SetBlend LIGHTBLEND
		SetColor 64,255,255
		SetAlpha .25
		SetScale .25,.25
		DrawImage image,x,y
		SetScale .25,-.25
		DrawImage image,x,y+(ImageHeight(image)/4)
		SetScale 1,1	
	End Method
	
	Method highlightSelected()
		SetBlend LIGHTBLEND
		SetColor 255,255,0
		
		If selectFade > 0 
			SetAlpha selectFade * .5
			selectFade :- .04
		Else
			SetAlpha .5
		End If
		SetScale selectedImageScale,-selectedImageScale
		DrawImage selectedImage,x+selectedImageXOffset,y+(ImageHeight(selectedImage)*selectedImageScale)

		' reflection
		SetAlpha (GetAlpha() *.5)
		SetScale selectedImageScale,selectedImageScale*.5
		If selectedReflectionImage <> Null
			DrawImage selectedReflectionImage,x+selectedImageXOffset,y+(ImageHeight(selectedReflectionImage)*selectedImageScale)+1
		Else
			DrawImage selectedImage,x+selectedImageXOffset,y+(ImageHeight(selectedImage)*selectedImageScale)+1
		End If
		
		
		SetScale 1,1	
	End Method
	
	Method selectItem()
		If selected = False
			' deselect any other selected menuItem in same group
			For Local mi:MenuItem = EachIn menuItems
				If mi.group = group Then mi.deselectItem()	
			Next 
			selected = True
		End If
	End Method
	
	Method deselectItem()
		If selected = True Then 
			selected = False
			selectFade = 1.0
		End If
	End Method

End Type


' -----------------------------------------

Function checkCorruption()
	For Local x:Int = 0 To width-1
		For Local y:Int = 0 To height-1
			Local p:Particle = px[x,y]
			If p <> Null
				If p.x <> x Or p.y <> y Then 				RuntimeError("corruption detected @ " + x + " "+ y + " " + p.toString())
			End If
		Next
	Next
End Function

' -----------------------------------------
Function userInput()

	If MouseX() > (xOrigin)-20 And MouseX() < (xOrigin+width)+20 
		draw(selectedElement)
	End If

	Local oldSelectedElement:Int = selectedElement	
	If KeyHit(KEY_1)
		selectedElement = ELEMENT_EMPTY
	Else If KeyHit(KEY_2)
		selectedElement = ELEMENT_WATER
	Else If KeyHit(KEY_3)
			selectedElement = ELEMENT_CONCRETE
	Else If KeyHit(KEY_4)
		    selectedElement = ELEMENT_SALT
	Else If KeyHit(KEY_5)
		    selectedElement = ELEMENT_MAGMA
	Else If KeyHit(KEY_6)
		  	selectedElement = ELEMENT_FIRE
	Else If KeyHit(KEY_7)
		    selectedElement = ELEMENT_OIL
	Else If KeyHit(KEY_8)
		    selectedElement = ELEMENT_SAND
	Else If KeyHit(KEY_9)
			selectedElement = ELEMENT_SMOKE
	End If
	If selectedElement <> oldSelectedElement Then reflectStateInMenu()
	
	Local modifier:Int = False
	If KeyDown(KEY_LCONTROL) Or KeyDown(KEY_LSYS) Or KeyDown(KEY_RSYS)
		modifier = True
	End If
	
	If modifier And KeyHit(KEY_N)
		initParticles() 
	Else If modifier And KeyHit(KEY_O)
		MenuItem.setSelectedItem(ITEMGROUP_CONTROL, 0)
		loadSelectMode = True
	Else If modifier And KeyHit(KEY_S)
		MenuItem.setSelectedItem(ITEMGROUP_CONTROL, 1)
		saveSelectMode = True
	Else If KeyHit(KEY_D)
			drawMode = (drawMode + 1) Mod MAX_DRAWMODES
			reflectStateInMenu()
	Else If KeyHit(KEY_R)
			renderMode = (renderMode + 1) Mod MAX_RENDERMODES
	Else If KeyHit(KEY_S)
			selectedPenSize = (selectedPenSize + 1) Mod MAX_PEN_SIZE
			reflectStateInMenu()
	Else If KeyHit(KEY_SPACE)
			simulationRunning = Not simulationRunning
			reflectStateInMenu()
	Else If KeyHit(KEY_G)  reverseGravity()
	Else If KeyHit(KEY_V)  vSync = vSync ~ 1
	Else If KeyHit(KEY_B)  displayBackground = Not displayBackground
	Else If KeyHit(KEY_F1) initParticles()
	Else If KeyHit(KEY_F2) drawPreset(1)
	Else If KeyHit(KEY_F3) drawPreset(0)
	Else If KeyHit(KEY_F12) And savedGamePixmap <> Null Then Particle.initFromPixmap(savedGamePixmap)
	Else If KeyHit(KEY_C) checkCorruption()
	End If
	
End Function

Function displayZoom()

	Local cursorX:Int = MouseX()-xOrigin
	Local cursorY:Int = MouseY()-yOrigin

	Local tmpXOrigin:Int = xOrigin
	Local tmpYOrigin:Int = yOrigin
	Local tmpRenderMode:Int = renderMode
	
	'xOrigin = (xOrigin + width + 10)-cursorX
	'yOrigin = (GraphicsHeight()-yOrigin-128)-cursorY
	renderMode = RENDERMODE_BLOB
	
	SetColor 0,0,255
	SetScale .25,.25
	DrawImage backgroundImage,GraphicsWidth()-(xOrigin/2), GraphicsHeight()-(yOrigin+64)
	SetColor 64,64,64
	SetScale .25,-.25
	DrawImage backgroundImage,GraphicsWidth()-(xOrigin/2), GraphicsHeight()-(yOrigin+64)

	
	Particle.preRender()
	
	Local xoffset:Int = GraphicsWidth() - (cursorX+30)
	Local yoffset:Int = (GraphicsHeight()-(centerY-halfHeight))-(cursorY+22)
	
	For Local x:Int = cursorX-21 To cursorX+21
		For Local y:Int = cursorY-21 To cursorY+21
			If x >= 0 And x < width And y >= 0 And y < height
				If px[x,y] <> Null
					xOrigin = xoffset + ((x-cursorX-21)*2)
					yOrigin = yoffset + ((y-cursorY-21)*2)
					Local p:Particle = px[x,y]
					p.render()
				End If
			End If
		Next
	Next
	
	Particle.postRender()
	SetScale 1,1
	SetColor 255,255,255
	xOrigin = tmpXOrigin
	yOrigin = tmpYOrigin
	renderMode = tmpRenderMode
	
	SetAlpha .25 + ((frame Mod 8)/16.0)
	DrawRect xOrigin+width+10+62-(penSizes[selectedPenSize]*1.25),GraphicsHeight()-yOrigin-64-(penSizes[selectedPenSize]*1.25),(penSizes[selectedPenSize]*2.5),(penSizes[selectedPenSize]*2.5)


	If MouseX() > xOrigin And MouseX() < xOrigin+width And MouseY() > yOrigin And MouseY() < yOrigin+height
		Local p:Particle = px[MouseX()-xOrigin,MouseY()-yOrigin]
		Local eType:String = "Air"
		If p <> Null eType = elementLabels[p.element]
		
		SetAlpha .5
		SetBlend LIGHTBLEND
		DrawText eType, GraphicsWidth()-xOrigin+12,GraphicsHeight()-yOrigin-126
		'If p <> Null DrawText p.toString(), 0,30
	End If	 

	

End Function

Function oldDisplayZoom()
	GrabImage(zoomImage,MouseX()-32,MouseY()-32)
	SetScale 2,2
	SetBlend SOLIDBLEND
	SetColor 255,255,255
	DrawImage zoomImage,xOrigin+width+10,GraphicsHeight()-yOrigin-128
	SetScale 1,1
	
	If MouseX() > xOrigin And MouseX() < xOrigin+width And MouseY() > yOrigin And MouseY() < yOrigin+height
		Local p:Particle = px[MouseX()-xOrigin,MouseY()-yOrigin]
		Local eType:String = "Air"
		If p <> Null eType = elementLabels[p.element]
		
		SetAlpha .5
		SetBlend LIGHTBLEND
		DrawText eType, GraphicsWidth()-xOrigin+12,GraphicsHeight()-yOrigin-126
		'If p <> Null DrawText p.toString(), 0,30
	End If	 
	SetAlpha 1
	SetBlend SOLIDBLEND

End Function

Function updateMenu()
	
	SetColor 255,255,255	
   SetOrigin 0,0
   SetScale 1,1
   SetBlend ALPHABLEND
   SetAlpha 1
   DrawImage menuImage,0,yOrigin

	
	For Local mi:MenuItem = EachIn menuItems
		mi.update()
		If mi.hasJustBeenSelected()
			Select mi.group
			
				Case ITEMGROUP_ELEMENTS
					 selectedElement = mi.id
					
				Case ITEMGROUP_BRUSH
				    selectedPenSize = mi.id Mod 3
					drawMode = mi.id / 3
					
				Case ITEMGROUP_CONTROL
					Select mi.id
					    Case 0
							loadSelectMode = True
					    Case 1
							saveSelectMode = True
					    Case 2
						Case 3 
							initParticles()
							reflectStateInMenu()
						Case 4 simulationRunning = False
						Case 5 simulationRunning = True
					End Select					
			End Select 
		End If
	Next

End Function

Function reverseGravity()

	For Local i:Int = 0 To MAX_ELEMENTS-1
		gravity[i] = -gravity[i]
	Next

End Function

' --------------------------------------------------------------------

Function createGradientImage:TImage(w:Int,h:Int)
	Local image:TImage = CreateImage(w,h)
	Local pix:TPixmap = LockImage(image)
	
	Cls
	SetBlend SOLIDBLEND
	For Local y:Int = 0 To h
		Local c:Int = 255*(y/Float(h))
		SetColor c,c,c
		DrawLine 0,y,w,y
	Next
	
	GrabImage image,0,0
	UnlockImage image
	Return image

End Function

Function createBlobImage:TImage()
	Local image:TImage = CreateImage(8,8)
	Local pix:TPixmap = LockImage(image)
	Cls
	SetColor 255,255,255
	SetBlend LIGHTBLEND
	SetAlpha .2
	SetScale 1,1
	DrawOval 0,0,8,8
	DrawOval 1,1,6,6
	DrawOval 2,2,4,4
	DrawOval 3,3,2,2 
	
	GrabImage image,0,0
	UnlockImage image
	SetImageHandle image,4,4
	Return image

End Function

Function createButtonBackgroundImage:TImage(buttonWidth:Int=100, buttonHeight:Int=24, lowerCMult:Float = 1.0)

	Local image:TImage = CreateImage(buttonWidth,buttonHeight)
	Local pix:TPixmap = LockImage(image)
    Cls
	SetColor 255,255,255
	SetBlend SOLIDBLEND
	DrawOval 0,0,buttonWidth/4,buttonHeight
	DrawOval buttonWidth - (buttonWidth/4),0,buttonWidth/4,buttonHeight
	DrawRect buttonWidth/8,0,buttonWidth - (buttonWidth/4),buttonHeight 
	
	SetBlend SHADEBLEND
	
	For Local y:Int = 0 To buttonHeight/1.65
		Local c:Int = 225-(y*3)
		SetColor c,c,c
		For Local x:Int = 0 To buttonWidth/8
			Plot buttonWidth/8-x, y-((x*x)*.004)
			Plot (buttonWidth-buttonWidth/8)+x,y-((x*x)*.004)
		Next
		DrawLine 1+(buttonWidth/8),y,buttonWidth-(buttonWidth/8)-1,y
	Next

	For Local y:Int = buttonHeight/1.65 To buttonHeight
		Local c:Int = ((y*4)-205)*lowerCMult
		SetColor c,c,c
		For Local x:Int = 0 To buttonWidth/8
			Plot buttonWidth/8-x, y-((x*x)*.004)
			Plot (buttonWidth-buttonWidth/8)+x,y-((x*x)*.004)
		Next
		DrawLine 1+(buttonWidth/8),y,buttonWidth-(buttonWidth/8)-1,y
	Next
	
	GrabImage image,0,0
	UnlockImage image
	Return image

End Function

Function createMenuImage:TImage()

    Local buttonImage:TImage = createButtonBackgroundImage((xOrigin-40)*4,22*4)
	Local smallButtonImage:TImage = createButtonBackgroundImage((xOrigin-40),22*4)
	Local smallButtonReflectionImage:TImage = createButtonBackgroundImage((xOrigin-40),22*4,0.0)
	Local image:TImage = CreateImage(xOrigin,GraphicsHeight())
	Local pix:TPixmap = LockImage(image)
	Cls

	' elements menu	
	SetColor 255,255,255
	SetBlend SOLIDBLEND
	DrawText "ELEMENTS",(xOrigin-TextWidth("ELEMENTS"))/2,6

	Local y:Int = 30	
	Local i:Int = 0
	For Local label:String = EachIn elementLabels
	
		If elementCreatable[i] = True
		    SetScale .25,-.25
			SetBlend SOLIDBLEND
			SetColor elementRed[i]*.75,elementGreen[i]*.75,elementBlue[i]*.75
			DrawImage buttonImage,20,y-4+22
			SetColor 255,255,255
			SetScale .25,.25
			SetAlpha .25
			SetBlend LIGHTBLEND
			DrawImage buttonImage,20,y-4
	
			SetScale 1,1
			SetBlend ALPHABLEND
			SetAlpha .5		
			SetColor 0,0,0
			DrawText label, 1+((xOrigin) - TextWidth(label))/2, y+1
			SetAlpha .25
			DrawText label, 2+((xOrigin) - TextWidth(label))/2, y+2
	
			SetAlpha .75
			SetColor 225,225,225
			DrawText label, ((xOrigin) - TextWidth(label))/2, y
	
			MenuItem.Create(20, y+40, xOrigin-40, 24, i, buttonImage, ITEMGROUP_ELEMENTS, blobImage, 1.5, xOrigin-30)
			y:+24
		End If
		i:+1
	Next

	' brush menu	
	Local buttonWidth:Int = ImageWidth(smallButtonImage)/4
	Local buttonHeight:Int = ImageHeight(smallButtonImage)/4
	Local x:Int = (xOrigin-((buttonWidth+2)*3))/2

	For Local i:Int = 0 To MAX_PEN_SIZE-1

        ' small button background
		SetBlend ALPHABLEND
		SetAlpha .75
		SetColor 228,168,228
		SetScale .25,.25
		DrawImage smallButtonImage, x+((2+buttonWidth)*i),y+24
		DrawImage smallButtonImage, x+((2+buttonWidth)*i),y+48
		DrawImage smallButtonImage, x+((2+buttonWidth)*i),y+72
		
		SetAlpha .45
		DrawImage smallButtonImage, x+((2+buttonWidth)*i),y+120
		DrawImage smallButtonImage, x+((2+buttonWidth)*i),y+144

		' small button label shadows
		SetScale 1,1
		SetColor 0,0,0
		SetAlpha .5
		drawButtonLabels(i, x+1,y+1, buttonWidth, buttonHeight)
		SetColor 0,0,0
		drawButtonLabels(i, x+2,y+2, buttonWidth, buttonHeight)

		' small button label 
		SetColor 220,220,220
		SetAlpha .75
		drawButtonLabels(i, x,y, buttonWidth, buttonHeight)
		
		' small button 
		SetBlend ALPHABLEND
		SetAlpha .35
		SetColor 255,0,0
		SetScale .25,-.25
		DrawImage smallButtonImage, x+((2+buttonWidth)*i),y+24+22
		DrawImage smallButtonImage, x+((2+buttonWidth)*i),y+48+22
		DrawImage smallButtonImage, x+((2+buttonWidth)*i),y+72+22

		SetColor 128,255,0
		DrawImage smallButtonImage, x+((2+buttonWidth)*i),y+120+22
		DrawImage smallButtonImage, x+((2+buttonWidth)*i),y+144+22
		
		MenuItem.Create(x+(i*(buttonWidth+2)), y+24+44, buttonWidth-2, buttonHeight, (DRAWMODE_PEN*3)+i, smallButtonImage, ITEMGROUP_BRUSH)
		MenuItem.Create(x+(i*(buttonWidth+2)), y+48+44, buttonWidth-2, buttonHeight, (DRAWMODE_LINE*3)+i, smallButtonImage, ITEMGROUP_BRUSH)
		MenuItem.Create(x+(i*(buttonWidth+2)), y+72+44, buttonWidth-2, buttonHeight, (DRAWMODE_CIRCLE*3)+i, smallButtonImage, ITEMGROUP_BRUSH,Null, .25, 0, smallButtonReflectionImage)

		MenuItem.Create(x+(i*(buttonWidth+2)), y+120+44, buttonWidth-2, buttonHeight, i, smallButtonImage, ITEMGROUP_CONTROL)
		MenuItem.Create(x+(i*(buttonWidth+2)), y+144+44, buttonWidth-2, buttonHeight, i+3, smallButtonImage, ITEMGROUP_CONTROL, Null, .25, 0, smallButtonReflectionImage)

	Next

	GrabImage image,0,0
	UnlockImage image
	Return image


End Function

Function reflectStateInMenu()
	
	MenuItem.setSelectedItem(ITEMGROUP_ELEMENTS, selectedElement)
	MenuItem.setSelectedItem(ITEMGROUP_BRUSH, (drawMode*3) + selectedPenSize)
	If simulationRunning = False Then 
		MenuItem.setSelectedItem(ITEMGROUP_CONTROL,4)
	Else
		MenuItem.setSelectedItem(ITEMGROUP_CONTROL,5)
	End If
End Function

		
Function drawButtonLabels(i:Int, x:Int,y:Int, w:Int, h:Int)

	Local red:Int,green:Int,blue:Int
	GetColor(red,green,blue)

	' pen
	DrawRect x+(w/2)+(i*(w+2))-(penSizes[i]/2), y+24+(h/2)-(penSizes[i]/2), penSizes[i],penSizes[i]
	' line
	SetLineWidth penSizes[i]	
	DrawLine x+(w/4)+(i*(w+2)), y+48+(h-(h/4))-2, x+(w-(w/4))+(i*(w+2)), y+48+(h/4)+2
	
	
	Select i
		Case 0
		
			' load
			DrawPoly([ Float(x+8),Float(y+120+(h-12)), Float(x+(w-8)),Float(y+120+(h-12)), Float(x+(w/2)),Float(y+120+4)]) 
			DrawPoly([ Float(x+4),Float(y+120+(h-4)), Float(x+(w-4)),Float(y+120+(h-4)), Float(x+(w-6)),Float(y+120+(h-8)), Float(x+6),Float(y+120+(h-8))  ]) 
			SetColor 0,0,0
			DrawPoly([ Float(x+6),Float(y+120+(h-5)), Float(x+(w-6)),Float(y+120+(h-5)), Float(x+(w-7)),Float(y+120+(h-7)), Float(x+7),Float(y+120+(h-7))  ])
			SetColor red,green,blue
			DrawRect  x+11, y+120+(h-11), 4,5

			
			' circle
			SetScale 1+(penSizes[i]/2),1+(penSizes[i]/2)
			DrawCircle( x+(w/2)+(i*(w+2))-(penSizes[i]/2)-1, y+72+(h/2)-(penSizes[i]/2), w/4 )
			SetScale 1,1

			' new
			SetLineWidth 1
			SetColor 0,0,128
			DrawRect x+((w+2)*0)+6,y+144+5,12,10
			SetColor 225,225,225
			DrawLine x+((w+2)*0)+6,  y+144+5,  x+((w+2)*1)-10, y+144+5
			DrawLine x+((w+2)*0)+6,  y+144+15, x+((w+2)*1)-10, y+144+15
			DrawLine x+((w+2)*1)-10, y+144+5,  x+((w+2)*1)-10, y+144+15
			DrawLine x+((w+2)*0)+6,  y+144+5,  x+((w+2)*0)+6, y+144+15

		Case 1
			' save
			DrawPoly([ Float(x+w+2+4),Float(y+120+(h-4)), Float(x+w+2+(w-4)),Float(y+120+(h-4)), Float(x+w+2+(w-6)),Float(y+120+(h-8)), Float(x+w+2+6),Float(y+120+(h-8))  ]) 
			SetColor 0,0,0
			DrawPoly([ Float(x+w+2+6),Float(y+120+(h-5)), Float(x+w+2+(w-6)),Float(y+120+(h-5)), Float(x+w+2+(w-7)),Float(y+120+(h-7)), Float(x+w+2+7),Float(y+120+(h-7))  ])
			SetColor red,green,blue
			DrawRect  x+w+2+11, y+120+5, 4,5
			DrawPoly([Float(x+w+2+8),Float(y+120+11), Float(x+w+2+(w-8)),Float(y+120+11), Float(x+w+2+(w/2)),Float(y+120+(h-5))]) 


			'circle
			SetScale 1+(penSizes[i]/2),1+(penSizes[i]/2)
			DrawCircle( x+(w/2)+(i*(w+2))-(penSizes[i]/2), y+72+(h/2)-(penSizes[i]/2), w/4 )
			SetScale 1,1

			' pause
			DrawRect x+w+2+(w/4)+1, y+144+(h/4), 5,h/2
			DrawRect x+w+2+(w-(w/2))+1, y+144+(h/4), 5,h/2
		Case 2
			'circle
			SetScale 1+(penSizes[i]/2),1+(penSizes[i]/2)
			DrawCircle( x+(w/2)+(i*(w+2))-(penSizes[i]/2), y+72+(h/2)-(penSizes[i]/2), w/4 )
			SetScale 1,1
			
			' play
			DrawPoly([Float(x+w+w+2+2+(w/4)+3), Float(y+144+(h/4)), Float(x+w+w+2+2+(w/4)+3), Float(y+144+(h-(h/4))),Float( x+w+w+2+2+(w-(w/2))+6), Float(y+144+(h/2))]) 
			 
	End Select
	
		
End Function

Function DrawCircle(x:Int,y:Int,radius:Int, stepinc:Float=6.0)
	Local angle:Float = 0
	While (angle < 360.0)
		DrawRect x+(Sin(angle)*radius),y+(Cos(angle)*radius),1,1
		angle :+ stepinc 
	Wend
End Function


Function drawPreset(preset:Int)
	Select preset
		Case 1
			For Local x:Int = width/4 To width-(width/4)
				For Local y:Int = 50 To 100
					Particle.Create(selectedElement,x,y)
				Next
			Next

		Case 0
			For Local x:Int = 0 To width-1 Step 8
				For Local y:Int = 0 To height -1 Step 8
					If Rand(0,8) = 0
						For Local z:Int = 0 To 7
							Particle.Create(selectedElement,x+z,y+z)
							Particle.Create(selectedElement,x+z+1,y+z)
						Next
						'For Local z:Int = 0 To 7
						'	Particle.Create(ELEMENT_GRAPHITE,x+z,y+3)
						'Next
					Else If Rand(0,8) = 0
						For Local z:Int = 0 To 7
							Particle.Create(selectedElement,(8+x)-z,y+z)
							Particle.Create(selectedElement,(9+x)-z,y+z)
						Next
						'For Local z:Int = 0 To 7
						'	Particle.Create(ELEMENT_GRAPHITE,x,y+z)
						'Next
					End If
				Next
			Next
	End Select
End Function

Function countFps:Int()
	fpsHistory[frame Mod MAX_FPS_HISTORY] = 1000.0 / (MilliSecs() - fpstime)
	fpstime = MilliSecs()
	Local fpsTotal:Int
	For Local fps:Int = EachIn fpsHistory
		fpsTotal :+ fps
	Next
	Return fpsTotal / MAX_FPS_HISTORY
End Function

Function draw(element:Int)

	Local currentMouseX:Int = MouseX() - xOrigin 
	Local currentMouseY:Int = MouseY() - yOrigin
	Local mouseReleased:Int = False

	If MouseDown(1)

		If drag = False ' start dragging
			dragFromX = currentMouseX
			dragFromY = currentMouseY
			drag = True
		End If
	
	Else 

		If drag = True
			mouseReleased = True
			drag = False
		End If
	
	End If
	
	Local halfPenSize:Int = penSizes[selectedPenSize]/2
	SetBlend LIGHTBLEND
	SetAlpha .25 + ((frame Mod 8)/16.0)
	Select renderMode	
		Case RENDERMODE_PIXEL
			DrawRect currentMouseX+xOrigin-halfPenSize,currentMouseY+yOrigin-1-halfPenSize,penSizes[selectedPenSize],penSizes[selectedPenSize]
		Default
			DrawRect currentMouseX+xOrigin-halfPenSize-1,currentMouseY+yOrigin-1-halfPenSize,penSizes[selectedPenSize]+1,penSizes[selectedPenSize]+1
	End Select	
	
   Select drawMode

		Case DRAWMODE_LINE

			If mouseReleased = True
				Local x:Float = dragFromX
				Local y:Float = dragFromY
				Local steps:Float = Sqr((Abs(currentMouseX-x) * Abs(currentMouseX-x)) + (Abs(currentMouseY-y) * Abs(currentMouseY-y)) )
				Local xInc:Float = (currentMouseX-x)/steps
				Local yInc:Float = (currentMouseY-y)/steps
			
				For Local i:Int = 0 To steps*2
					For Local n:Int = -halfPenSize To halfPenSize
		        		Particle.Create(element,x+(yInc*n),y+(-xInc*n))
					Next
					x :+ (xInc*.5)
					y :+ (yInc*.5)
			   Next
			
			Else If drag = True
				SetBlend LIGHTBLEND
				SetAlpha .25 + ((frame Mod 8)/16.0)
				SetLineWidth penSizes[selectedPenSize]
				DrawLine dragFromX+xOrigin,dragFromY+yOrigin-1,currentMouseX+xOrigin,currentMouseY+yOrigin-1
				SetLineWidth 1
			End If
	
		Case DRAWMODE_PEN
		
			If drag = True
				Local MouseXSpeed:Float = (currentMouseX-prevMouseX)*.1
				If MouseXSpeed > 1.0 MouseXSpeed = 1.0
				If MouseXSpeed < -1.0 MouseXSpeed = -1.0
				Local MouseYSpeed:Float = (currentMouseY-prevMouseY)*.1
				If MouseYSpeed > 1.0 MouseYSpeed = 1.0
				If MouseYSpeed < -1.0 MouseYSpeed = -1.0
				
				If (currentMouseX = dragFromX) And (currentMouseY = dragFromY)

					For Local xx:Int=-halfPenSize To halfPenSize
						For Local yy:Int=-halfPenSize To halfPenSize
				        	Particle.Create(element,xx+currentMouseX,yy+currentMouseY,MouseXSpeed,MouseYSpeed) 
				   		Next
					Next
				
				Else 

					Local x:Float = dragFromX
					Local y:Float = dragFromY
					Local steps:Float = Sqr((Abs(currentMouseX-x) * Abs(currentMouseX-x)) + (Abs(currentMouseY-y) * Abs(currentMouseY-y)) )
					Local xInc:Float = (currentMouseX-x)/steps
					Local yInc:Float = (currentMouseY-y)/steps
				
					For Local i:Int = 0 To steps*2
						For Local n:Int = -halfPenSize To halfPenSize
			        		Particle.Create(element,x+(yInc*n),y+(-xInc*n),MouseXSpeed,MouseYSpeed)
						Next
						x :+ (xInc*.5)
						y :+ (yInc*.5)
				   Next

				End If
								
				dragFromX = currentMouseX
				dragFromY = currentMouseY
				
				
			End If
			
		Case DRAWMODE_CIRCLE

			If mouseReleased = True
				Local x:Float = dragFromX
				Local y:Float = dragFromY
				Local radius:Float = Sqr((Abs(currentMouseX-x) * Abs(currentMouseX-x)) + (Abs(currentMouseY-y) * Abs(currentMouseY-y)) )
				Local angleInc:Float = 32/(Abs(radius)+.0001)
				Local angle:Float = 0.0
				
				While (angle < 360.0)
				
					Local xx:Float = Sin(angle) * radius
					Local yy:Float = Cos(angle) * radius
			
					For Local xxx:Int=-halfPenSize To halfPenSize
						For Local yyy:Int=-halfPenSize To halfPenSize
				        	Particle.Create(element,x+xx+xxx+1, y+yy+yyy+1) 
				   		Next
					Next
					
					angle :+ angleInc
				
				Wend 
			
			Else If drag = True
				Local radius:Float = Sqr((Abs(currentMouseX-dragFromX) * Abs(currentMouseX-dragFromX)) + (Abs(currentMouseY-dragFromY) * Abs(currentMouseY-dragFromY)) )
				SetBlend LIGHTBLEND
				SetAlpha .25 + ((frame Mod 8)/16.0)
				SetScale penSizes[selectedPenSize],penSizes[selectedPenSize]
				DrawCircle(dragFromX+xOrigin-halfPenSize,dragFromY+yOrigin-halfPenSize,radius, Min(6.0 * (64/(Abs(radius)+.0001)),6.0) )
				SetScale 1,1
			End If

	End Select
	
	prevMouseX = currentMouseX
	prevMouseY = currentMouseY


End Function

Function initParticles()

	Particle.reset()
	
	' concrete walls
	For Local x:Int = 1 To width-1
		Particle.Create(ELEMENT_CONCRETE,x,1)
		Particle.Create(ELEMENT_CONCRETE,x,height-1)
	Next
	For Local y:Int = 1 To height-1
		Particle.Create(ELEMENT_CONCRETE,1,y)
		Particle.Create(ELEMENT_CONCRETE,width-1,y)
	Next


	
End Function

'   try and create output directory If it doesn't exist already
Function createSaveGameDir()
	Local dir:Int = ReadDir(OUTPUT_DIRECTORY)
	If Not dir Then 
		CreateDir(OUTPUT_DIRECTORY,True)
	Else
		CloseDir(dir)
	End If
End Function

Function screenshot()

	Local filename:String = OUTPUT_DIRECTORY+"/elemental_"+MilliSecs()+".jpg"
	Local screengrab:TPixmap = GrabPixmap(0,0,GraphicsWidth(),GraphicsHeight())
	SavePixmapJPeg(screengrab,filename,100)

End Function


Function savePixmap(pixmap:TPixmap)

	createSaveGameDir()
	Local filename:String = OUTPUT_DIRECTORY+"/elemental_"+MilliSecs()+".png"
	SavePixmapPNG(pixmap,filename)

End Function

Function rgbToString:String(red:Byte,green:Byte,blue:Byte)
	Return Hex( (red Shl 24) + (green Shl 16) + (blue Shl 8))
End Function


Type SavedGame

	Global savedGames:TList = CreateList()
	Field pixmap:TPixmap
	Field sprite:TSprite
	Field file:TFile
	Field deleting:Float = -1
	
	Function Create:SavedGame(file:TFile)
		Local sg:SavedGame = New SavedGame
		sg.pixmap   = PixmapWindow(LoadPixmap(file.path+"/"+file.filename),0,0,width,height)
		sg.file = file
		sg.sprite   = TSprite.Create(LoadImage(sg.pixmap, MIPMAPPEDIMAGE))
		sg.file.isLoaded = True
		ListAddLast savedGames,sg
		Return sg
	End Function
	
	Function updateAll(selectedIndex:Int, lsmooth:Float, sliding:Int)
		Local i:Int = 0
		Local deletedCount:Int = 0
		For Local sg:SavedGame = EachIn savedGames
			sg.update(i, selectedIndex, deletedCount, lsmooth, sliding)
			If sg.deleting >= 0 deletedCount :+1 
			i :+ 1
		Next
	End Function
	
	Function getSelectedSavedGame:SavedGame(selectedIndex:Int)
		Local i:Int = 0
		For Local sg:SavedGame = EachIn savedGames
			If i = selectedIndex And sg.deleting < 0
				Return sg
			End If
			If sg.deleting < 0 i :+ 1
		Next	
	End Function
	
	Function deleteAll()
		TSprite.deleteAll()
		ClearList(savedGames)
	End Function
	
	Function renderAll()
		SetBlend ALPHABLEND
		SetAlpha 1
		SetColor 255,255,255
		TSprite.renderAll()
	End Function
	
	Method update(index:Int, selectedIndex:Int, deletedCount:Int, lsmooth:Float, sliding:Int)
		' calculate x,y,z coords of all sprites
		Local smooth:Float = lsmooth 
		If sliding = False smooth = 0.0
		sprite.x = ((index-deletedCount-selectedIndex+smooth)+(deletedCount*Abs(lsmooth)))*2 
		sprite.y = 0
		sprite.z = -3.5 + (Cos((sprite.x)*22.5*.5)*2)
		
		If deleting > 0
			sprite.x = 0
			sprite.z = -1.5 - ((1.0-deleting)*4)
			deleting :- .02
			If deleting <= 0 Then
				DeleteFile(file.path +"/"+ file.filename) 
				'Print "removed " + file.path + "/" + file.filename		
				remove()
			End If 
		End If 
	End Method
	
	Method remove()
		sprite.remove()
		savedGames.remove(Self)		
	End Method	

End Type

Function selectSavedFile:TPixmap()

	FlushMouse()
	FlushKeys()
	MoveMouse(centerX,centerY)

	Local selected:TPixmap = Null
	Local files:TList = readImageDirFiles(OUTPUT_DIRECTORY)
	Local fileCount:Int = CountList(files)
	If fileCount = 0 Return selected
	SortList files
	
	For Local f:TFile = EachIn files
		SavedGame.Create(f)
	Next

	Local lsmoothX:Float = 0.0
	Local selecting:Int = True
	Local sliding:Int = False
	
	While (selecting)
		Local sg:SavedGame = SavedGame.getSelectedSavedGame(selectedImageIndex)
		Cls
		SetBlend LIGHTBLEND
		SetScale 1,1
		SetAlpha .5
		SetOrigin 0,0
		SetColor 255,255,255 
		DrawText "Select File",centerX-(TextWidth("Select File")/2),yOrigin+6
		SetAlpha 0.5-Abs(lsmoothX)
		DrawText sg.file.filename, centerX-(TextWidth(sg.file.filename)/2), (halfHeight/4)+halfHeight/2

		SetAlpha .75
		SetColor 96,96,64
		SetScale 2,.125
		DrawImage backgroundImage,centerX,(centerY-(height/16))

		SavedGame.updateAll(selectedImageIndex, lsmoothX, sliding)		
		SavedGame.renderAll()
						
		If lsmoothX <> 0.0 lsmoothX :* .92
		
		If Abs(lsmoothX) < .01
			sliding = False
		End If
		If Abs(lsmoothX) < .1
			If selectedImageIndex > 0
				If KeyHit(KEY_LEFT) Then FlushKeys() ; MoveMouse(centerX,centerY) ; selectedImageIndex :-1 ; lsmoothX = -1.0 ; sliding = True 
				If KeyHit(KEY_UP) Then FlushKeys() ; MoveMouse(centerX,centerY) ; selectedImageIndex = 0 ; lsmoothX = -1.0 ; sliding = True
				If MouseX() <= 0 Then selectedImageIndex :-1 ; lsmoothX = -1.0 ; sliding = True 
			End If
			If selectedImageIndex < fileCount-1
				If KeyHit(KEY_RIGHT) Then FlushKeys() ; MoveMouse(centerX,centerY) ; selectedImageIndex :+1 ; lsmoothX = 1.0 ; sliding = True 
				If KeyHit(KEY_DOWN) Then FlushKeys() ; MoveMouse(centerX,centerY) ; selectedImageIndex = fileCount-1 ; lsmoothX = 1.0 ; sliding = True
				If MouseX() >= GraphicsWidth()-1 Then selectedImageIndex :+1 ; lsmoothX = 1.0 ; sliding = True 
			End If
		End If

		If KeyHit(KEY_DELETE) And sg.deleting < 0 Then
			sliding = False 
			sg.deleting = 1.0
			lsmoothX = 1.0
			fileCount :- 1
			If fileCount <= 0 Then 
				selecting = False
			Else If selectedImageIndex > fileCount-1 Then 
				selectedImageIndex = fileCount-1
				lsmoothX = -1.0
				sliding = True
			End If
		End If   

		If KeyHit(KEY_ENTER) Or MouseHit(1) Then selected = sg.pixmap ; selecting = False
		If KeyHit(KEY_ESCAPE) selecting = False
		If KeyDown(KEY_LCONTROL) And KeyHit(KEY_G) Then 	screenshot()

		
		Flip
		Delay 1
	Wend

	SavedGame.deleteAll()
			
	Return selected

End Function

Function readImageDirFiles:TList(path:String)

	Local files:TList = CreateList()
    Local dirFiles:String[]
    dirFiles=LoadDir(path)

    For Local filename:String = EachIn dirFiles
      
        Local fileExtension:String = Lower(Right(filename,4))
        If fileExtension = ".png"
            ListAddLast files, TFile.Create(path,filename,FileTime(path+"/"+filename),FileSize(path+"/"+filename) )
        End If  
    Next
    
	Return files
End Function



